namespace MoreLinq.Test;

using System;
using System.Collections.Generic;
using NUnit.Framework;
using NUnit.Framework.Constraints;

public enum SourceKind
{
    Sequence,
    BreakingList,
    BreakingReadOnlyList,
    BreakingCollection,
    BreakingReadOnlyCollection
}

static class SourceKinds
{
    public static readonly IEnumerable<SourceKind> Sequence = [
        SourceKind.Sequence
    ];

    public static readonly IEnumerable<SourceKind> Collection = [
        SourceKind.BreakingCollection,
        SourceKind.BreakingReadOnlyCollection
    ];

    public static readonly IEnumerable<SourceKind> List = [
        SourceKind.BreakingList,
        SourceKind.BreakingReadOnlyList
    ];
}

static partial class TestExtensions
{
    /// <summary>
    /// Just to make our testing easier so we can chain the assertion call.
    /// </summary>

    internal static void AssertSequenceEqual<T>(this IEnumerable<T> actual, IEnumerable<T> expected) =>
        Assert.That(actual, Is.EqualTo(expected));

    /// <summary>
    /// Make testing even easier - a params array makes for readable tests :)
    /// The sequence should be evaluated exactly once.
    /// </summary>

    internal static void AssertSequenceEqual<T>(this IEnumerable<T> actual, params T[] expected) =>
        Assert.That(actual, Is.EqualTo(expected));

    internal static void AssertSequence<T>(this IEnumerable<T> actual, params IResolveConstraint[] expectations)
    {
            var i = 0;
            foreach (var item in actual)
            {
                Assert.That(i, Is.LessThan(expectations.Length), "Actual sequence has more items than expected.");
                var expectation = expectations[i];
                Assert.That(item, expectation, "Unexpected element in sequence at index " + i);
                i++;
            }
            Assert.That(i, Is.EqualTo(expectations.Length), "Actual sequence has fewer items than expected.");
        }

    internal static IEnumerable<string> GenerateSplits(this string str, params char[] separators)
    {
            foreach (var split in str.Split(separators))
                yield return split;
        }

    internal static IEnumerable<T> ToSourceKind<T>(this IEnumerable<T> input, SourceKind sourceKind) =>
#pragma warning disable IDE0072 // Add missing cases
        sourceKind switch
#pragma warning restore IDE0072 // Add missing cases
        {
            SourceKind.Sequence => input.Select(x => x),
            var kind => input.ToList().AsSourceKind(kind)
        };

    internal static IEnumerable<T> AsSourceKind<T>(this List<T> input, SourceKind sourceKind) =>
        sourceKind switch
        {
            SourceKind.Sequence => input.Select(x => x),
#pragma warning disable IDE0028 // Simplify collection initialization (would change semantics)
            SourceKind.BreakingList => new BreakingList<T>(input),
            SourceKind.BreakingReadOnlyList => new BreakingReadOnlyList<T>(input),
            SourceKind.BreakingCollection => new BreakingCollection<T>(input),
            SourceKind.BreakingReadOnlyCollection => new BreakingReadOnlyCollection<T>(input),
#pragma warning restore IDE0028 // Simplify collection initialization
            _ => throw new ArgumentException(null, nameof(sourceKind))
        };
}
